package fr.labri.macchiato.core.model;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import fr.labri.macchiato.core.model.Cart;
import fr.labri.macchiato.core.technical.FrameworkException;
import fr.labri.macchiato.core.technical.Identifiable;
import fr.labri.macchiato.core.technical.MObservable;
import fr.labri.macchiato.core.technical.MObserver;

/**
 * A project has a list of {@link Product}s ordered by category, and has a
 * budget
 * 
 * @author Abdoulaye Diallo
 * 
 */

public class Project implements Identifiable<String>, MObserver {

	private static final String ERROR_NOT_UNIQUE_NAME = "Un panier avec le même nom existe déjà";
	private static final String ERROR_UNKNOWN_CATEGORY = "Le panier avec le nom demandé n'existe pas";

	private String errorBudgetOverflow() {
		return "Vous avez atteint la limite de votre budget qui est :"
				+ getBudget() + "€";
	}

	private Map<String, Cart> carts;
	private String name;
	private double budget;

	Project() {
	}

	public Project(String name, double budget) {
		this(name);
		setBudget(budget);
		initCartsBudget();
	}

	public Project(String name) {
		checkProjectName(name);
		carts = new HashMap<String, Cart>();
		this.setName(name);
	}

	private void checkProjectName(String name) {
		if (name == null || name.trim().isEmpty()) {
			throw new FrameworkException(
					"Le projet ne doit pas avoir un nom vide");
		}
	}

	/***
	 * Set each cart budget in the project (initial value is 0)
	 * 
	 */
	private void initCartsBudget() {
		for (Cart cart : carts.values())
			cart.setBudget(0);
	}

	/**
	 * @return the project's budget
	 */
	public double getBudget() {
		return budget;
	}

	/**
	 * @return the project's remaining budget
	 */
	public double getRemainingBudget() {
		double budgetTmp = getBudget();
		for (Cart cartgory : carts.values())
			budgetTmp -= cartgory.getBudget();
		return budgetTmp;
	}

	/**
	 * set a budget to a project
	 * 
	 * @param budget
	 */
	public void setBudget(double budget) {
		this.budget = budget;
	}

	/**
	 * 
	 * @return the project's name
	 */
	public String getName() {
		return this.name;
	}

	/**
	 * set the project's name
	 * 
	 * @param name
	 */
	public void setName(String name) {
		this.name = name;
	}

	@Override
	public boolean equals(Object object) {
		if (!(object instanceof Project))
			return false;

		Project project = (Project) object;
		return project.getId().equals(getId());
	}

	/**
	 * add a {@link Product} to the corresponding {@link Cart}
	 * 
	 * @param product
	 * @param category
	 *            : {@link Cart}'s name
	 */
	public void addProduct(Product product, String category) {
		this.getCategory(category).addProduct(product);
	}

	/**
	 * add various {@link Product} to the corresponding {@link Cart}
	 * 
	 * @param products
	 * @param category
	 *            : {@link Cart}'s name
	 */
	public void addProducts(List<Product> products, String category) {
		this.getCategory(category).addProducts(products);
	}

	/**
	 * Remove a {@link Product} from a {@link Cart}
	 * 
	 * @param product
	 * @param category
	 *            : {@link Cart}'s name
	 */
	public void removeProduct(Product product, String category) {
		this.getCategory(category).removeProduct(product);
	}

	/**
	 * 
	 * @return The mapping between the project's {@link Cart} and their IDs
	 */
	public Map<String, Cart> getCarts() {
		return carts;
	}

	/**
	 * Set the mapping between the project's {@link Cart} and their IDs
	 * 
	 * @param carts
	 */
	public void setCarts(Map<String, Cart> carts) {
		this.carts = carts;
	}

	/**
	 * 
	 * @param category
	 * @return the {@link Cart} which correspond to the ID passed as parameter
	 */
	public Cart getCategory(String category) {
		if (!carts.containsKey(category))
			throw new FrameworkException(ERROR_UNKNOWN_CATEGORY);
		return this.carts.get(category);
	}

	/**
	 * Add a {@link Cart} to the project
	 * 
	 * @param cart
	 */
	public void addCategory(Cart cart) {
		String cartName = cart.getName();
		if (this.carts.containsKey(cartName))
			throw new FrameworkException(ERROR_NOT_UNIQUE_NAME);

		if (cart.getBudget() != 0 && cart.getBudget() > getRemainingBudget())
			throw new FrameworkException(errorBudgetOverflow());

		this.carts.put(cartName, cart);
	}

	/**
	 * Add a {@link Cart} with a specified budget to the project
	 * 
	 * @param category
	 * @param cartBudget
	 */
	public void addCategory(String category, double cartBudget) {
		Cart cart = new Cart(category);
		cart.addObserver(this);
		cart.setBudget(cartBudget);
		this.addCategory(cart);

	}

	private void checkRemainingBudget() {
		if (getRemainingBudget() < 0)
			throw new FrameworkException(errorBudgetOverflow());
	}

	/**
	 * Remove a {@link Cart} from the project
	 * 
	 * @param category
	 */
	public void removeCategory(String category) {
		this.carts.remove(category);
	}

	public String getId() {
		return getName();
	}

	public void update(MObservable o, Object arg) {
		if (o instanceof Cart) {
			checkRemainingBudget();
		}
	}

}
